#include "stdafx.h"

#include <QWebSocket>

#include "Constants.h"
#include "Session.h"
#include "TCpu.h"

#include "ClientManager.h"
#include "TSearcherWindow.h"
#include "TaskManager.h"
#include <assert.h>

ClientManager::ClientManager()
{
}


ClientManager::~ClientManager()
{
}


//Session ClientManager::newTask(const QString &taskName, const QVariantMap &taskArg)
//{
//    auto channel = Session::newChannel();
//    TaskManager::instance()->newTask(channel, taskName, taskArg);
//
//    Session session(popFirstActiveClient()->websocket, channel);
//    return session;
//}

void ClientManager::onNewConnection(QWebSocket *client)
{
    
    QMetaObject::Connection c = QObject::connect(client, &QWebSocket::textMessageReceived, [=](const QString & message)
    {
        onUnknownClientTextMessageRecieved(client, message);
    });

    // We don't have to listen on "disconnected" message, because we will check whenever user aquire a new client.
}

QVariantMap pickJson(QString *json, QJsonParseError *error)
{
    int braces = 0;
    for (int i = 1, n = json->length(); i < n; i++)
    {
        auto c = json->at(i);
        if (c == '}')
        {
            if (braces-- == 0)
            {
                i++;
                auto first = json->left(i);
                auto rest = json->mid(i);

                *json = rest;
                return QJsonDocument::fromJson(first.toUtf8(), error).object().toVariantMap();
            }
        }
        else if (c == '{')
        {
            braces++;
        }
    }
    return QJsonDocument::fromJson(json->toUtf8(), error).object().toVariantMap();
}

void ClientManager::onUnknownClientTextMessageRecieved(QWebSocket *client, QString message)
{
    QJsonParseError error;
    QVariantMap json = pickJson(&message, &error);
    if (error.error == QJsonParseError::NoError)
    {
        assert(json.count("channel") > 0);
        auto type = json.value("type").toString();
        auto channel = json.value("channel").toInt();
        auto browser = json.value("browser").toString();
        if (type == Constants::strings.mainClientLogin)
        {
            // Main Client login, let us register it.
            registerNewClient(client, browser);

            // parse the rest of the message
            onClientTextMessageRecieved(client, message);
        }
    }
    else
    {
        // It isn't out client, close its connection.
        client->close();

        // make auto-deleted.
        QObject::connect(client, &QWebSocket::disconnected, client, &QWebSocket::deleteLater);
    }
}

void ClientManager::onClientTextMessageRecieved(QWebSocket *client, QString message)
{
    QString msg_bakcup = message;
    clock_t start = clock();
    QJsonParseError error;
    while (message.length())
    {
        auto tb = TCpu::instance()->tasks;
        QVariantMap vmap = pickJson(&message, &error);
        if (error.error == QJsonParseError::NoError)
        {
            assert(vmap.count("channel") > 0);
            auto type = vmap.value("type").toString();
            auto channel = vmap.value("channel").toInt();
            auto browser = vmap.value("browser").toString();
            TCpu::instance()->on(channel, type, vmap.value("arg").toMap());
        }
        else
        {
            TCpu::instance()->on(Constants::numbers.ERROR_CHANNEL,
                Constants::strings.messageParseError,
                vmap.value("arg").toMap());
        }

    }
    clock_t dur = clock() - start;
    return;
}

void ClientManager::onClientDisconnected()
{
    if (!hasActiveClients())
        TSearcherWindow::instance()->disableFunctions();
}

ClientManager * ClientManager::instance()
{
    if (_instance == nullptr)
        _instance = new ClientManager;
    return _instance;
}

Client *ClientManager::popFirstActiveClient()
{
    assert(this->size() > 0);

    do
    {
        Client *c = this->pop_front();
        assert(nullptr != c);
        if (c->websocket->state() == QAbstractSocket::ConnectedState)
        {
            // Since it is still useful for next task, re-push it into queue
            push_back(c);
            return c;
        }

        // Since it will no longer be used again, just delete it to free memory.
        delete c;
    } while (this->size());
    return nullptr;
}

void ClientManager::registerNewClient(QWebSocket * client, const QString& browser)
{
    // Enable its next communication
    QObject::disconnect(client, &QWebSocket::textFrameReceived, 0, 0);
    QObject::connect(client, &QWebSocket::textMessageReceived, [=](const QString & message)
    {
        onClientTextMessageRecieved(client, message);
    });
    QObject::connect(client, &QWebSocket::disconnected, [this]()
    {
        onClientDisconnected();
    });

    // Push it into clients quque
    this->push_back(new Client(client, browser));

    TSearcherWindow::instance()->enableFunctions();
}

void ClientManager::push_back(Client *client)
{
    clientsQueue.push_back(client);
}

void ClientManager::push_front(Client *client)
{
    clientsQueue.push_front(client);
}

Client * ClientManager::pop_front()
{
    Client *c = clientsQueue.front();
    clientsQueue.pop_front();

    return c;
}

size_t ClientManager::size()
{
    return clientsQueue.size();
}

bool ClientManager::hasActiveClients()
{
    return firstActiveClient() != nullptr;
}

Client * ClientManager::firstActiveClient()
{
    if (!this->size())
        return nullptr;

    do
    {
        Client *c = this->pop_front();
        assert(nullptr != c);
        if (c->websocket->state() == QAbstractSocket::ConnectedState)
        {
            // Since it is still useful for next task, re-push it into queue
            push_front(c);
            return c;
        }

        // Since it will no longer be used again, just delete it to free memory.
        delete c;
    } while (this->size());
    return nullptr;
}

ClientManager * ClientManager::_instance = nullptr;